package com.liuya.safe.entitle.service.impl;

import com.liuya.base.AbstractBaseService;
import com.liuya.base.BaseModel;
import com.liuya.common.exception.EntityExistException;
import com.liuya.common.I18N;
import com.liuya.common.MyHashMap;
import com.liuya.db.DBLevelException;
import com.liuya.db.util.DBUtil;
import com.liuya.safe.util.Constants;
import com.liuya.safe.exception.AuthException;
import com.liuya.safe.model.SafePrivilege;
import com.liuya.safe.entitle.service.PrivilegeManagerService;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

@Service("privilegeManagerService")
public class PrivilegeManagerServiceImpl extends AbstractBaseService<SafePrivilege> implements PrivilegeManagerService, Serializable {

    private SafePrivilege businessPrivilegeTree;
    private SafePrivilege nonRolePrivilegeTree;
    private Map allBusinessPrivilegesMap;
    private Map allNonRolePrivilegesMap;
    private boolean businessPrivilegeTreeChanged = true;
    private boolean nonRolePrivilegeTreeChanged = true;
    private String appName;
    private String tableName;

    public PrivilegeManagerServiceImpl() {
    }

    public PrivilegeManagerServiceImpl(String appName) {
        this.appName = appName;
        this.tableName = appName + "_privilege";
    }

    public void addReservedPrivilege(Locale locale) {
        try {
            addSafeAdminPrivilege(locale);
            addPolicyAdminPrivilege(locale);
            addAssignRoleToUserPrivilege(locale);
            addRoleAdminPrivilege(locale);
            businessPrivilegeTreeChanged = true;
        } catch (EntityExistException e) {
            throw new AuthException(e);
        }
    }

    private void addSafeAdminPrivilege(Locale locale) throws EntityExistException {
        SafePrivilege privilege = new SafePrivilege();
        privilege.setType(SafePrivilege.BUSINESS_PRIVILEGE);
        privilege.setIsLeaf(false);
        privilege.setId(SafePrivilege.AUTH_ADMIN_ID);
        privilege.setPid(SafePrivilege.BUSINESS_PRIVILEGE_TREE_ROOT_ID);
        privilege.setName(I18N.getAuthMessage(locale, Constants.SAFE_ADMIN));
        privilege.setOrderNum(0);
        mapper.insert(privilege);
    }

    private void addPolicyAdminPrivilege(Locale locale) throws EntityExistException {
        SafePrivilege privilege = new SafePrivilege();
        privilege.setType(SafePrivilege.BUSINESS_PRIVILEGE);
        privilege.setIsLeaf(true);
        privilege.setId(SafePrivilege.POLICY_ADMIN_ID);
        privilege.setPid(SafePrivilege.AUTH_ADMIN_ID);
        privilege.setDescription(I18N.getAuthMessage(locale, Constants.POLICY_ADMIN_DESCRIPTION));
        privilege.setName(I18N.getAuthMessage(locale, Constants.POLICY_ADMIN));
        privilege.setConstantName("POLICY_ADMIN");
        privilege.setOrderNum(1);
        mapper.insert(privilege);
    }

    private void addAssignRoleToUserPrivilege(Locale locale) throws EntityExistException {
        SafePrivilege privilege = new SafePrivilege();
        privilege.setType(SafePrivilege.BUSINESS_PRIVILEGE);
        privilege.setIsLeaf(true);
        privilege.setId(SafePrivilege.ASSIGN_ROLE_TO_USER_ID);
        privilege.setPid(SafePrivilege.AUTH_ADMIN_ID);
        privilege.setName(I18N.getAuthMessage(locale, Constants.ASSIGN_ROLE_TO_USER));
        privilege.setConstantName("ASSIGN_ROLE_TO_USER");
        privilege.setOrderNum(2);
        mapper.insert(privilege);
    }

    private void addRoleAdminPrivilege(Locale locale) throws EntityExistException {
        SafePrivilege privilege = new SafePrivilege();
        privilege.setType(SafePrivilege.BUSINESS_PRIVILEGE);
        privilege.setIsLeaf(true);
        privilege.setId(SafePrivilege.ROLE_ADMIN_ID);
        privilege.setPid(SafePrivilege.AUTH_ADMIN_ID);
        privilege.setName(I18N.getAuthMessage(locale, Constants.ROLE_ADMIN));
        privilege.setConstantName("ROLE_ADMIN");
        privilege.setOrderNum(3);
        mapper.insert(privilege);
    }

    public SafePrivilege addPrivilege(SafePrivilege pvlg) throws EntityExistException {
        String pid = pvlg.getPid();
        SafePrivilege parentPvlg = null;
        if (pvlg.getType() == SafePrivilege.BUSINESS_PRIVILEGE) {
            buildBusinessPrivilegeTree();
            parentPvlg = (SafePrivilege) allBusinessPrivilegesMap.get(new Integer(pid));
        } else if (pvlg.getType() == SafePrivilege.NON_ROLE_PRIVILEGE) {
            buildNonRolePrivilegeTree();
            parentPvlg = (SafePrivilege) allNonRolePrivilegesMap.get(new Integer(pid));
        }

        int orderNum = -1;
        // get the greatest orderNum
        if (parentPvlg != null && parentPvlg.getChildren() != null && parentPvlg.getChildren().size() > 0) {
            for (Iterator iter = parentPvlg.getChildren().iterator(); iter.hasNext();) {
                int temp = ((SafePrivilege) iter.next()).getOrderNum();
                orderNum = orderNum >= temp ? orderNum : temp;
            }
        }

        pvlg.setId(newPrivilegeId());
        pvlg.setOrderNum(orderNum + 1);

        mapper.insert(pvlg);
        if (pvlg.getType() == SafePrivilege.BUSINESS_PRIVILEGE) {
            businessPrivilegeTreeChanged = true;
        } else if (pvlg.getType() == SafePrivilege.NON_ROLE_PRIVILEGE) {
            nonRolePrivilegeTreeChanged = true;
        }
        return pvlg;
    }

    private String newPrivilegeId() {
        try {
            return DBUtil.getSequenceNextVal("privilege_id").toString();
        } catch (SQLException e) {
            throw new DBLevelException(e);
        }
    }

    public void deletePrivilege(String id) {
        if (id == "") {
            throw new AuthException("Cannot delete reserved privilege.");
        }
        // delete role-privilege relation infos
//        RoleManager roleManager = Factory.getRoleManager(appName);
//        roleManager.deleteRolePrivilegeByPrivilege(id);
        // delete privilege
        SafePrivilege pvlg = getPrivilege(id);
        if (pvlg == null) {
            return;
        }

        mapper.deleteByPrimaryKey(pvlg.getId());
        if (pvlg.getType() == SafePrivilege.BUSINESS_PRIVILEGE) {
            businessPrivilegeTreeChanged = true;
        } else if (pvlg.getType() == SafePrivilege.NON_ROLE_PRIVILEGE) {
            nonRolePrivilegeTreeChanged = true;
        }
    }

    public Collection getAllBusinessPrivileges() {
        buildBusinessPrivilegeTree();
        return new ArrayList(allBusinessPrivilegesMap.values());
    }

    public Collection getAllBusinessPrivilegesFromDb() {
//        FieldWhereElement emt = new FieldWhereElement();
//        emt.setColumn(table.getColumns()[8]);
//        emt.setCompartor(SingleValueComparator.EQUAL);
//        emt.setValue(new Integer(Privilege.BUSINESS_PRIVILEGE));
//        SelectCondition cdtn = new SelectCondition();
//        cdtn.setWhereElement(emt);
//        return selector.select(cdtn, null);

        Map params = new HashMap();
        params.put("type", SafePrivilege.BUSINESS_PRIVILEGE);
        return mapper.selectAll(params);
    }

    public Collection getAllNonRolePrivileges() {
        buildNonRolePrivilegeTree();
        return new ArrayList(allNonRolePrivilegesMap.values());
    }

    public Collection getAllNonRolePrivilegesFromDb() {
//        FieldWhereElement emt = new FieldWhereElement();
//        emt.setColumn(table.getColumns()[8]);
//        emt.setCompartor(SingleValueComparator.EQUAL);
//        emt.setValue(new Integer(Privilege.NON_ROLE_PRIVILEGE));
//        SelectCondition cdtn = new SelectCondition();
//        cdtn.setWhereElement(emt);
//        return selector.select(cdtn, null);

        Map params = new HashMap();
        params.put("type", SafePrivilege.NON_ROLE_PRIVILEGE);
        return mapper.selectAll(params);
    }

    public Collection getLikelyPrivileges(String name) {
        buildBusinessPrivilegeTree();
        buildNonRolePrivilegeTree();

        Collection result = new LinkedList();
        for (Iterator iter = allBusinessPrivilegesMap.values().iterator(); iter.hasNext();) {
            SafePrivilege pvlg = (SafePrivilege) iter.next();
            if (pvlg.getName().contains(name)) {
                result.add(pvlg);
            }
        }

        for (Iterator iter = allNonRolePrivilegesMap.values().iterator(); iter.hasNext();) {
            SafePrivilege pvlg = (SafePrivilege) iter.next();
            if (pvlg.getName().contains(name)) {
                result.add(pvlg);
            }
        }

        return result;
    }

    public Collection getLikelyPrivilegesByUrl(String url) {
        buildBusinessPrivilegeTree();
        buildNonRolePrivilegeTree();

        List result = new LinkedList();
        for (Iterator iter = allBusinessPrivilegesMap.values().iterator(); iter.hasNext();) {
            SafePrivilege pvlg = (SafePrivilege) iter.next();
            if (pvlg.getUrl() != null && pvlg.getUrl().contains(url)) {
                result.add(pvlg);
            }
        }

        for (Iterator iter = allNonRolePrivilegesMap.values().iterator(); iter.hasNext();) {
            SafePrivilege pvlg = (SafePrivilege) iter.next();
            if (pvlg.getUrl() != null && pvlg.getUrl().contains(url)) {
                result.add(pvlg);
            }
        }

        Comparator comp = new Comparator() {
            public int compare(Object arg0, Object arg1) {
                SafePrivilege p0 = (SafePrivilege) arg0;
                SafePrivilege p1 = (SafePrivilege) arg1;
                String url0 = p0.getUrl();
                String url1 = p1.getUrl();

                if (url0 == null) {
                    url0 = "";
                }
                if (url1 == null) {
                    url1 = "";
                }

                return url0.compareTo(url1);
            }
        };

        Collections.sort(result, comp);
        return result;
    }

    public SafePrivilege getPrivilege(String id) {
        buildBusinessPrivilegeTree();
        buildNonRolePrivilegeTree();
        SafePrivilege nodeFromTree0 = (SafePrivilege) allBusinessPrivilegesMap.get(new Integer(id));
        SafePrivilege nodeFromTree1 = (SafePrivilege) allNonRolePrivilegesMap.get(new Integer(id));
        return (nodeFromTree0 != null ? nodeFromTree0 : nodeFromTree1);
    }

    public void updatePrivilege(SafePrivilege pvlg) throws EntityExistException {
        pvlg.setStatus(1);
        mapper.updateByPrimaryKey(pvlg);
        if (pvlg.getType() == SafePrivilege.BUSINESS_PRIVILEGE) {
            businessPrivilegeTreeChanged = true;
        } else if (pvlg.getType() == SafePrivilege.NON_ROLE_PRIVILEGE) {
            nonRolePrivilegeTreeChanged = true;
        }
    }

    public void movePrivilege(SafePrivilege privilege, SafePrivilege target, int newOrderNum) {
        try {
            if (privilege.getId() == "0" || Integer.valueOf(privilege.getId()) < 0) {
                throw new AuthException("Cannot move reserved privilege.");
            }

            privilege = getPrivilege(privilege.getId());
            target = getPrivilege(target.getId());
            if (privilege.getType() != target.getType()) {
                throw new AuthException("Cannot move privilege out of the tree.");
            }
            if (target.getIsLeaf()) {
                throw new AuthException("Canot move privilege to a leaf node.");
            }
            if (privilege == target || isCascadeChild(privilege.getId(), target.getId())) {
                throw new AuthException("This move will produce a cycle.");
            }

            Collection children = getChildren(target);
            ArrayList list = new ArrayList();
            if (children != null) {
                list.addAll(children);
            }

            // If it still be a child of the original parent node
            if (privilege.getPid() == target.getId()) {
                list.remove(privilege);
            }

            privilege.setPid(target.getId());

            // reset orderNum(s), and update to db
            list.add(newOrderNum, privilege);
            for (int i = 0, size = list.size(); i < size; i++) {
                SafePrivilege p = (SafePrivilege) list.get(i);
                p.setOrderNum(i);

                mapper.updateByPrimaryKey(p);
            }

            if (target.getType() == SafePrivilege.BUSINESS_PRIVILEGE) {
                businessPrivilegeTreeChanged = true;
            } else if (target.getType() == SafePrivilege.NON_ROLE_PRIVILEGE) {
                nonRolePrivilegeTreeChanged = true;
            }
        } catch (Exception e) {
            throw new DBLevelException(e);
        }
    }

    public void deletePrivilegeCascade(String nodeId) {
        SafePrivilege current = getPrivilege(nodeId);
        Collection children = getAllChildren(current);
        // delete current node
        deletePrivilege(current.getId());
        // delete cascade children nodes
        Iterator itr = children.iterator();
        while (itr.hasNext()) {
            BaseModel child = (BaseModel) itr.next();
            deletePrivilege(child.getId());
        }

        if (current.getType() == SafePrivilege.BUSINESS_PRIVILEGE) {
            businessPrivilegeTreeChanged = true;
        } else if (current.getType() == SafePrivilege.NON_ROLE_PRIVILEGE) {
            nonRolePrivilegeTreeChanged = true;
        }
    }

    private Collection getAllChildren(BaseModel current) {
        Set allChildren = new HashSet();
        // direct children
        Collection children = current.getChildren();
        allChildren.addAll(children);
        // find children of children
        Iterator itr = children.iterator();
        while (itr.hasNext()) {
            BaseModel child = (BaseModel) itr.next();
            // cascade
            allChildren.addAll(getAllChildren(child));
        }
        return allChildren;
    }

    public Collection getChildren(SafePrivilege privilege) {
        SafePrivilege parent = getPrivilege(privilege.getId());
        return parent.getChildren();
    }

    public SafePrivilege getParent(SafePrivilege privilege) {
        SafePrivilege child = getPrivilege(privilege.getId());
        return (SafePrivilege) child.getParent();
    }

    public SafePrivilege getBusinessPrivilegeTree() {
        buildBusinessPrivilegeTree();
        return businessPrivilegeTree;
    }

    public SafePrivilege getNonRolePrivilegeTree() {
        buildNonRolePrivilegeTree();
        return nonRolePrivilegeTree;
    }

    public SafePrivilege getTree(String nodeId) {
        return getPrivilege(nodeId);
    }

    public boolean isCascadeChild(String pId, String id) {
        BaseModel parent = getPrivilege(pId);
        BaseModel child = getPrivilege(id);

        if (parent != null && child != null) {
            while (child.getParent() != null) {
                if (parent.getId() == child.getPid())
                    return true;
                else
                    child = child.getParent();
            }
        }
        return false;
    }

    public boolean isChild(String pId, String id) {
        BaseModel parent = getPrivilege(pId);
        BaseModel child = getPrivilege(id);

        if (parent != null && child != null) {
            if (parent.getId() == child.getPid())
                return true;
        }
        return false;
    }

    private void buildTree(Collection privileges, Map allNodesMap) {
        // 1, Put all privilege into map
        Iterator itr = privileges.iterator();
        while (itr.hasNext()) {
            SafePrivilege privilege = (SafePrivilege) itr.next();
            allNodesMap.put(new Integer(privilege.getId()), privilege);
        }

        // 2, arrange child-parent relationship
        SafePrivilege child;
        SafePrivilege parent;
        itr = privileges.iterator();
        while (itr.hasNext()) {
            child = (SafePrivilege) itr.next();
            parent = (SafePrivilege) allNodesMap.get(new Integer(child.getPid()));
            if (parent != null) {
                parent.getChildren().add(child);
                child.setParent(parent);
            }
        }

        Comparator comp = new Comparator() {
            public int compare(Object arg0, Object arg1) {
                SafePrivilege p0 = (SafePrivilege) arg0;
                SafePrivilege p1 = (SafePrivilege) arg1;
                return p0.getOrderNum() - p1.getOrderNum();
            }
        };

        // 3, sort by orderNum
        for (Iterator iter = allNodesMap.values().iterator(); iter.hasNext();) {
            SafePrivilege p = (SafePrivilege) iter.next();
            Collection children = p.getChildren();

            if (children != null) {
                Collections.sort((List) children, comp);
            }
        }
    }

    private synchronized void buildBusinessPrivilegeTree() {
        if (!businessPrivilegeTreeChanged)
            return;
        businessPrivilegeTree = new SafePrivilege();
        businessPrivilegeTree.setName(SafePrivilege.TREE_ROOT_NAME);
        businessPrivilegeTree.setId(SafePrivilege.BUSINESS_PRIVILEGE_TREE_ROOT_ID);
        businessPrivilegeTree.setType(SafePrivilege.BUSINESS_PRIVILEGE);
        businessPrivilegeTree.setIsLeaf(false);
        businessPrivilegeTree.setPid(SafePrivilege.NULL_ROOT_ID);
        businessPrivilegeTree.setParent(null);
        businessPrivilegeTree.setOrderNum(0);
        allBusinessPrivilegesMap = new MyHashMap();

        Collection privileges = getAllBusinessPrivilegesFromDb();
        privileges.add(businessPrivilegeTree);
        buildTree(privileges, allBusinessPrivilegesMap);
        businessPrivilegeTreeChanged = false;
    }

    private synchronized void buildNonRolePrivilegeTree() {
        if (!nonRolePrivilegeTreeChanged)
            return;
        nonRolePrivilegeTree = new SafePrivilege();
        nonRolePrivilegeTree.setName(SafePrivilege.TREE_ROOT_NAME);
        nonRolePrivilegeTree.setId(SafePrivilege.NON_ROLE_PRIVILEGE_TREE_ROOT_ID);
        nonRolePrivilegeTree.setType(SafePrivilege.NON_ROLE_PRIVILEGE);
        nonRolePrivilegeTree.setIsLeaf(false);
        nonRolePrivilegeTree.setPid(SafePrivilege.NULL_ROOT_ID);
        nonRolePrivilegeTree.setParent(null);
        nonRolePrivilegeTree.setOrderNum(0);
        allNonRolePrivilegesMap = new MyHashMap();
        Collection privileges = getAllNonRolePrivilegesFromDb();
        privileges.add(nonRolePrivilegeTree);
        buildTree(privileges, allNonRolePrivilegesMap);
        nonRolePrivilegeTreeChanged = false;
    }

}